package ai;


public class FuzzyEngineFactory {

	private static ResAllocFuzzyEngine resAllocFuzzyEngine;

	private FuzzyEngineFactory() {
		// no constructor
	}

	public static ResAllocFuzzyEngine getResAllocFuzzyEngine() {
		if (resAllocFuzzyEngine == null) {
			resAllocFuzzyEngine = new ResAllocFuzzyEngine();
		}
		return resAllocFuzzyEngine;
	}

	public static class ResAllocFuzzyEngine extends FuzzyEngine {

		public ResAllocFuzzyEngine() {

			super();
			// FUZZY VARIABLE PRIORITY
			FuzzyVariable priority = this.new FuzzyVariable("priority");
			MemberShipFunction priorityLow = this.new MemberShipFunction("low",
					(short) 0, (short) 0, AIConstant.FUZZY_PRIORITY_LOW,
					AIConstant.FUZZY_PRIORITY_MEDIUM);
			MemberShipFunction priorityMedium = this.new MemberShipFunction(
					"medium", AIConstant.FUZZY_PRIORITY_LOW,
					AIConstant.FUZZY_PRIORITY_MEDIUM,
					AIConstant.FUZZY_PRIORITY_MEDIUM,
					AIConstant.FUZZY_PRIORITY_HIGH);
			MemberShipFunction priorityHigh = this.new MemberShipFunction(
					"high", AIConstant.FUZZY_PRIORITY_MEDIUM,
					AIConstant.FUZZY_PRIORITY_HIGH, (short) 127, (short) 127);
			priority.addMemberShipFunction(priorityLow);
			priority.addMemberShipFunction(priorityMedium);
			priority.addMemberShipFunction(priorityHigh);
			this.addVariable(priority);

			// FUZZY VARIABLE DISTANCE
			FuzzyVariable distance = this.new FuzzyVariable("distance");
			MemberShipFunction distanceClosed = this.new MemberShipFunction(
					"closed", (short) 0, (short) 0,
					AIConstant.FUZZY_DISTANCE_CLOSED,
					AIConstant.FUZZY_DISTANCE_MEDIUM);
			MemberShipFunction distanceMedium = this.new MemberShipFunction(
					"medium", AIConstant.FUZZY_DISTANCE_CLOSED,
					AIConstant.FUZZY_DISTANCE_MEDIUM,
					AIConstant.FUZZY_DISTANCE_MEDIUM,
					AIConstant.FUZZY_DISTANCE_FAR);
			MemberShipFunction distanceFar = this.new MemberShipFunction("far",
					AIConstant.FUZZY_DISTANCE_MEDIUM,
					AIConstant.FUZZY_DISTANCE_FAR, (short) 127, (short) 127);
			distance.addMemberShipFunction(distanceClosed);
			distance.addMemberShipFunction(distanceMedium);
			distance.addMemberShipFunction(distanceFar);
			this.addVariable(distance);

			// FUZZY VARIABLE SCORE
			FuzzyVariable score = this.new FuzzyVariable("score");
			MemberShipFunction scoreLow = this.new MemberShipFunction("low",
					AIConstant.FUZZY_SCORE_LOW);
			MemberShipFunction scoreMedium = this.new MemberShipFunction(
					"medium", AIConstant.FUZZY_SCORE_MEDIUM);
			MemberShipFunction scoreHigh = this.new MemberShipFunction("high",
					AIConstant.FUZZY_SCORE_HIGH);
			score.addMemberShipFunction(scoreLow);
			score.addMemberShipFunction(scoreMedium);
			score.addMemberShipFunction(scoreHigh);
			// fuzzyEngine.addVariable(score);

			// FUZZY RULES
			this.addRule(this.new Rule(distanceClosed, scoreHigh));
			this.addRule(this.new Rule(distanceMedium, scoreMedium));
			this.addRule(this.new Rule(distanceFar, scoreLow));
			this.addRule(this.new Rule(priorityLow, scoreLow));
			this.addRule(this.new Rule(priorityMedium, scoreMedium));
			this.addRule(this.new Rule(priorityHigh, scoreHigh));

		}

		public short defuzzify(short priority, short distance) {
			double sum = 0;
			double weights = 0;

			((FuzzyVariable) variables.get(0)).reset();
			((FuzzyVariable) variables.get(0)).fuzzify(priority);
			((FuzzyVariable) variables.get(1)).reset();
			((FuzzyVariable) variables.get(1)).fuzzify(distance);

			for (byte i = 0; i < rules.size(); i++) {
				((Rule) rules.get(i)).reset();
				((Rule) rules.get(i)).applyRule();
				sum += ((Rule) rules.get(i)).activationDegree
						* ((Rule) rules.get(i)).defuzzify();
				weights += ((Rule) rules.get(i)).activationDegree;
			}
			return (short) (sum / weights);
		}

	}

}
